package com.draby.starfantasymaze.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;

import com.draby.starfantasymaze.R;
import com.draby.starfantasymaze.dto.Achievement;
import com.draby.starfantasymaze.exceptions.KeyRepeatException;

import org.jetbrains.annotations.NotNull;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class FileUtil {
    @SuppressLint("StaticFieldLeak")
    private static Context context = null;

    /**
     * 保存上下文
     * @param context 待保存的上下文
     */
    public static boolean saveContext(@NotNull Context context) {
        try {
            FileUtil.context = context;
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /* ------------------------------------ 文件相关的方法 ---------------------------------------- */
    /**
     * 读取文件内容
     * @param parent 父级目录
     * @param fileName 文件名
     * @return null: 未找到文件; 否则返回该文件内的所有内容
     */
    private static String readFile(String parent, String fileName) {
        FileInputStream fis = null;
        try {
            // 创建文件路径
            File file = new File(context.getExternalFilesDir(parent), fileName);
            fis = new FileInputStream(file);
            byte[] buff = new byte[1024];
            // 由于 baos 的 close 方法是空实现，所以调用也并没有关闭，无需调用(大概？)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len;
            while ((len = fis.read(buff)) > 0) {
                baos.write(buff, 0, len);
            }
            return baos.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 将内容保存到文件
     * @param parent 父级目录
     * @param fileName 文件名
     * @param content 文件内的所有内容
     * @return true: 文件保存成功; 否则返回 false
     */
    private static boolean saveFile(String parent, String fileName, String content) {
        FileOutputStream fos = null;
        try {
            File dir = context.getExternalFilesDir(parent);
            File file = new File(dir, fileName);
            fos = new FileOutputStream(file);
            // 清空文件内容再重写
            fos.flush();
            fos.write(content.getBytes());
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 是否有数据文件，有则表示不是第一次启动 app
     * @param parent 父级目录
     * @param fileName 文件名
     * @return 数据文件是否存在
     */
    public static boolean hasDataFile(String parent, String fileName) {
        return new File(context.getExternalFilesDir(parent), fileName).exists();
    }

    /* ------------------------------ data.txt 数据文件处理方法 ----------------------------------- */
    /**
     * 设置 key 的 value
     * @param parent 父级目录
     * @param fileName 文件名
     * @param key 唯一键
     * @param value 唯一键对应的值
     * @return true: 设置成功; 否则返回 false
     */
    public static boolean setValue(String parent, String fileName, String key, String value) {
        // 根据文件父目录与文件名获取文件内容
        String content = readFile(parent, fileName);
        if (content == null) {
            return false;
        }
        // 查找 key 的索引
        int idx = -1;
        try {
            idx = validKey(content, key);
        } catch (KeyRepeatException e) {
            e.printStackTrace();
            return false;
        }
        if (idx == -1) {
            // key 不存在则将键值对添加到文件最后
            content += (key + "=" + value + ";\n");
        } else {
            // 修改 key 对应的 value
            // 找到 value 的前后索引
            int leftIndex = content.indexOf("=", idx) + 1;
            int rightIndex = content.indexOf(";", leftIndex);
            String front = content.substring(0, leftIndex);
            String back = content.substring(rightIndex);
            content = front + value + back;
        }
        return saveFile(parent, fileName, content);
    }

    /**
     * 根据 key 获取 value
     * @param parent 父级目录
     * @param fileName 文件名
     * @param key 唯一键
     * @return null: 键不存在; 否则返回唯一键对应的值
     */
    public static String getValue(String parent, String fileName, String key) {
        // 根据文件父目录与文件名获取文件内容
        String content = readFile(parent, fileName);
        if (content == null) {
            return null;
        }
        // 查找 key 的索引并根据返回值判断 key 的存在情况
        int idx = -1;
        try {
            idx = validKey(content, key);
        } catch (KeyRepeatException e) {
            // 有多个重复的 key
            e.printStackTrace();
        }
        if (idx == -1) {
            // key 不存在
            return null;
        } else {
            // 找到 value 的前后索引
            int leftIndex = content.indexOf("=", idx) + 1;
            int rightIndex = content.indexOf(";", leftIndex);
            return content.substring(leftIndex, rightIndex);
        }
    }

    /**
     * 根据 key 删除数据
     * @param parent 父级目录
     * @param fileName 文件名
     * @param key 唯一键
     * @return true: 删除成功; 否则返回false
     */
    public static boolean removeValue(String parent, String fileName, String key) {
        // 根据文件父目录与文件名获取文件内容
        String content = readFile(parent, fileName);
        if (content == null) {
            return false;
        }
        // 查找 key 的索引
        int idx = -1;
        try {
            idx = validKey(content, key);
        } catch (KeyRepeatException e) {
            e.printStackTrace();
            return false;
        }
        if (idx == -1) {
            // 未找到 key, 返回删除成功
            return true;
        } else {
            // 找到键值对的边界索引(左边界为 idx)
            int rightIndex = content.indexOf("\n", idx) + 1;
            String front = content.substring(0, idx);
            String back = content.substring(rightIndex);
            content = front + back;
            return saveFile(parent, fileName, content);
        }
    }

    /**
     * 检查 key 是否合法
     * @param content 待检查的数据文件内容
     * @param key 唯一键
     * @return -1: 没有该键; 否则返回该键的索引位置
     * @throws KeyRepeatException 键重复异常
     */
    private static int validKey(String content, String key) throws KeyRepeatException {
        // 以存储格式缩小查找，只寻找 key 而不会寻找到 value
        String regex = key + "=";
        // 保存 key 出现的所有索引位置
        List<Integer> keyIndex = new ArrayList<>();
        int idx = content.indexOf(regex);
        // 循环寻找所有索引
        while (idx > -1) {
            keyIndex.add(idx);
            idx = content.indexOf(regex, idx + 1);
        }
        // 判断 index 集合内元素个数
        int count = keyIndex.size();
        if (count == 0) {
            // 集合为空
            return -1;
        } else if (count == 1) {
            // 集合内只有唯一的一个索引
            return keyIndex.get(0);
        } else {
            // 集合内有多个索引
            throw new KeyRepeatException("数据中有多个重复的 key 存在！");
        }
    }

    /* -------------------------- achievement.json 成就文件处理方法 ------------------------------- */
    /**
     * 读取所有成就
     * @param parent 父级目录
     * @param fileName 文件名
     * @return null: 未找到文件或解析出错; 否则返回该文件内的所有成就的列表
     */
    public static List<Achievement> getAllAchievements(String parent, String fileName) {
        String content = readFile(parent, fileName);
        if (content == null) {
            return null;
        } else {
            return JsonUtil.getAchievementsFromJson(content);
        }
    }

    /**
     * 根据 成就名称 来查找成就
     * @param parent 父级目录
     * @param fileName 文件名
     * @param name 成就名称
     * @return null: 未找到该成就; 否则返回该成就
     */
    public static Achievement getAchievementByName(String parent, String fileName, String name) {
        if (name == null) {
            return null;
        }
        Achievement achievement = null;
        // 先获取所有成就
        List<Achievement> achievements = getAllAchievements(parent, fileName);
        // 遍历成就列表，找到名称相同的成就
        for (Achievement achievement1 : achievements) {
            if (name.equals(achievement1.getName())) {
                achievement = achievement1;
                break;
            }
        }
        return achievement;
    }

    /**
     * 新增一个成就
     * @param parent 父级目录
     * @param fileName 文件名
     * @param achievement 成就对象
     * @return true: 添加成功; 否则返回 false
     */
    public static boolean addAchievement(String parent, String fileName, Achievement achievement) {
        // 获取当前的所有成就
        List<Achievement> achievements = getAllAchievements(parent, fileName);
        // 将新成就添加进去
        achievements.add(achievement);
        // 序列化为 json 字符串
        String content = JsonUtil.convertToJson(achievements);
        // 重新保存到文件中
        return saveFile(parent, fileName, content);
    }

    /**
     * 根据 成就名称 删除一个成就
     * @param parent 父级目录
     * @param fileName 文件名
     * @param name 成就名称
     * @return true: 删除成功; 否则返回 false
     */
    public static boolean deleteAchievementByName(String parent, String fileName, String name) {
        if (name == null) {
            return false;
        }
        int index = -1;
        // 获取成就列表
        List<Achievement> achievements = getAllAchievements(parent, fileName);
        // 遍历成就列表，找到名称相同的成就
        for (int i = 0; i < achievements.size(); i++) {
            if (name.equals(achievements.get(i).getName())) {
                index = i;
                break;
            }
        }
        // 未找到成就
        if (index == -1) {
            return false;
        }
        // 根据索引删除成就
        achievements.remove(index);
        // 序列化为 json 字符串
        String content = JsonUtil.convertToJson(achievements);
        // 保存到文件
        return saveFile(parent, fileName, content);
    }

    /**
     * 根据 成就名称 设置成就完成
     * @param parent 父级目录
     * @param fileName 文件名
     * @param name 成就名称
     * @return true: 更改成功; 否则返回 false
     */
    public static boolean finishAchievementByName(String parent, String fileName, String name) {
        if (name == null) {
            return false;
        }
        boolean found = false;
        // 获取成就列表
        List<Achievement> achievements = getAllAchievements(parent, fileName);
        // 循环遍历寻找 名称 与 name 相同的成就，并更新它的获取状态
        for (Achievement achievement : achievements) {
            if (name.equals(achievement.getName())) {
                found = true;
                achievement.setOwned(true);
                achievement.setGainTime(new Date());
                ToastUtil.showAchievement(context, achievement);
                break;
            }
        }
        // 未找到对应成就
        if (!found) {
            return false;
        }
        // 序列化并保存
        String content = JsonUtil.convertToJson(achievements);
        return saveFile(parent, fileName, content);
    }

    /**
     * 检查指定名称的成就是否已完成
     * @param parent 父级目录
     * @param fileName 文件名
     * @param name 成就名称
     * @return true: 已完成; 否则返回 false
     */
    public static boolean checkAchievementFinished(String parent, String fileName, String name) {
        Achievement achievement = getAchievementByName(parent, fileName, name);
        if (achievement == null) {
            return false;
        }
        return achievement.getOwned();
    }

    /* ---------------------------------- 初始化各种数据的方法 ------------------------------------ */
    /**
     * 初始化数据文件
     * @param parent 父级目录
     * @param fileName 文件名
     * @return true: 初始化成功; 否则返回 false
     */
    public static boolean initData(String parent, String fileName) {
        File dir = context.getExternalFilesDir(parent);
        if (!dir.exists()) {
            // 目录不存在
            if (!dir.mkdirs()) {
                // 创建目录失败
                return false;
            }
        }
        File file = new File(dir, fileName);
        if (!file.exists()) {
            // 文件不存在
            try {
                if (!file.createNewFile()) {
                    // 文件创建失败
                    return false;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        String content = getInitContent();
        return saveFile(parent, fileName, content);
    }

    /**
     * 初始化成就文件
     * @param parent 父级目录
     * @param fileName 文件名
     * @return true: 初始化成功; 否则返回 false
     */
    public static boolean initAchievements(String parent, String fileName) {
        File dir = context.getExternalFilesDir(parent);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                return false;
            }
        }
        File file = new File(dir, fileName);
        if (!file.exists()) {
            try {
                if (!file.createNewFile()) {
                    return false;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        List<Achievement> achievements = getInitAchievementList();
        String content = JsonUtil.convertToJson(achievements);
        return saveFile(parent, fileName, content);
    }

    /**
     * 获取初始数据
     * @return 初始数据字符串
     */
    private static String getInitContent() {
        return "nickname=LittleStar;\n" +
                "sign=个性签名;\n" +
                "birthday=2000-08-01;\n" +
                "firstLand=true;\n" +
                "recordEasy=0;\n" +
                "recordNormal=0;\n" +
                "recordHard=0;\n" +
                "recordInfinite=0;\n" +
                "infiniteDistance=0;\n" +
                "recordBirthday=0;\n" +
                "showTips=true;\n";
    }

    /**
     * 获取初始成就
     * @return 初始成就列表
     */
    private static List<Achievement> getInitAchievementList() {
        List<Achievement> achievements = new ArrayList<>();
        achievements.add(new Achievement(R.drawable.achi_draby, "BabyDragon", "首次开启应用即可获得一只 BabyDragon 😊"));
        achievements.add(new Achievement(R.drawable.achi_clearance, "来散步吧", "带着 BabyDragon 一起去散步吧"));
        achievements.add(new Achievement(R.drawable.achi_clearance, "这里怎么走", "帮助 BabyDragon 找到目的地的路吧"));
        achievements.add(new Achievement(R.drawable.achi_clearance, "完全迷路了", "找到 BabyDragon 并带它走到目的地吧"));
        achievements.add(new Achievement(R.drawable.achi_discard, "回不了家了", "在没有找到 BabyDragon 的情况下，独自通过迷失模式。于是路痴 BabyDragon 被留在了郊外...🤣", true));
        achievements.add(new Achievement(R.drawable.achi_birthday, "Happy Birthday!", "生日快乐哦~Keep Happy Every Day!\t\t(我不是故意设置这么麻烦的😂)", true));
        achievements.add(new Achievement(R.drawable.achi_limit_time, "小菜一碟", "在9s内通过散步模式"));
        achievements.add(new Achievement(R.drawable.achi_limit_time, "轻轻松松", "在27s内通过寻路模式"));
        achievements.add(new Achievement(R.drawable.achi_limit_time, "带不动", "在60s内找到 BabyDragon 并将其带到目的地"));
        achievements.add(new Achievement(R.drawable.achi_limit_time, "不带了", "在不找到BabyDragon的情况下，52s内通过迷失模式", true));
        return achievements;
    }
}
